﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Windows.Input;
using UMLSynthesizer.LangAnalyzer;
using UMLSynthesizer.LangAnalyzer.Elements;

namespace UMLSynthesizer.Tasks.ViewModel
{
    internal class ClassDiagramControlPanelViewModel : WorkspaceViewModel
    {
        #region Fields and RelayCommands

        IProjectElement projectElement;
        Action notifyRefresh;

        bool _isAutomaticRefreshEnabled;

        ReadOnlyCollection<ElementViewModel> _rootPackages;

        LanguageAnalyzer.Metrics _selectedClassMetric;
        LanguageAnalyzer.Metrics _selectedInterfaceMetric;
        LanguageAnalyzer.Metrics _selectedPackageMetric;
        LanguageAnalyzer.Metrics _selectedStructMetric;

        bool _showContainedClasses;
        bool _showContainedInterfaces;
        bool _showContainedPackages;
        bool _showContainedStructs;
        bool _showClassMembers;
        bool _showClassMethodParameters;
        bool _showClassMetrics;
        bool _showClassTypes;
        bool _showInterfaceMembers;
        bool _showInterfaceMethodParameters;
        bool _showInterfaceMetrics;
        bool _showInterfaceTypes;
        bool _showPackageMetrics;
        bool _showStructMembers;
        bool _showStructMethodParameters;
        bool _showStructMetrics;
        bool _showStructTypes;
        bool _showExternalDependencies;
        bool _showPrimitiveTypes;

        IElementBase _selectedElement;

        RelayCommand _refreshCommand;
        
        #endregion

        #region Public interface

        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="projectElement">The project to analyze</param>
        /// <param name="refreshEventCallback">
        /// The callback method to notify the need of a refresh
        /// </param>
        public ClassDiagramControlPanelViewModel(IProjectElement projectElement,
            Action notifyRefresh)
        {
            //Check parameters
            if (projectElement == null)
                throw new ArgumentNullException("The project can't be null");

            //Initialization
            this.projectElement = projectElement;
            this.notifyRefresh = notifyRefresh;
            AvailableClassMetrics = new LanguageAnalyzer.Metrics[]
            {
                LanguageAnalyzer.Metrics.CCC,
                LanguageAnalyzer.Metrics.CD,
                LanguageAnalyzer.Metrics.MCC
            };
            AvailableInterfaceMetrics = new LanguageAnalyzer.Metrics[]
            {
                LanguageAnalyzer.Metrics.CD
            };
            AvailablePackageMetrics = new LanguageAnalyzer.Metrics[]
            {
                LanguageAnalyzer.Metrics.ACD,
                LanguageAnalyzer.Metrics.CCD,
                LanguageAnalyzer.Metrics.NCCD
            };
            AvailableStructMetrics = AvailableClassMetrics;
            SelectedClassMetric = AvailableClassMetrics.FirstOrDefault();
            SelectedInterfaceMetric = AvailableInterfaceMetrics.FirstOrDefault();
            SelectedPackageMetric = AvailablePackageMetrics.FirstOrDefault();
            SelectedStructMetric = AvailableStructMetrics.FirstOrDefault();
        }

        #endregion

        #region Properties

        /// <summary>
        /// The list of available metrics for classes
        /// </summary>
        public IEnumerable<LanguageAnalyzer.Metrics> AvailableClassMetrics { get; private set; }
        /// <summary>
        /// The list of available metrics for interfaces
        /// </summary>
        public IEnumerable<LanguageAnalyzer.Metrics> AvailableInterfaceMetrics { get; private set; }
        /// <summary>
        /// The list of available metrics for packages
        /// </summary>
        public IEnumerable<LanguageAnalyzer.Metrics> AvailablePackageMetrics { get; private set; }
        /// <summary>
        /// The list of available metrics for structs
        /// </summary>
        public IEnumerable<LanguageAnalyzer.Metrics> AvailableStructMetrics { get; private set; }

        public bool IsAutomaticRefreshEnabled
        {
            get { return _isAutomaticRefreshEnabled; }
            set
            {
                if (value == _isAutomaticRefreshEnabled)
                    return;
                _isAutomaticRefreshEnabled = value;
                OnPropertyChanged("IsAutomaticRefreshEnabled");
            }
        }

        public ReadOnlyCollection<ElementViewModel> RootPackages
        {
            get
            {
                if (_rootPackages == null)
                    _rootPackages = new List<ElementViewModel>(new ElementViewModel[]
                        {
                            new ElementViewModel(projectElement, NotifyElementSelection,
                                false, false, true, false)
                        }).AsReadOnly();
                return _rootPackages;
            }
        }

        /// <summary>
        /// Current selected class metric
        /// </summary>
        public LanguageAnalyzer.Metrics SelectedClassMetric
        {
            get { return _selectedClassMetric; }
            set
            {
                if (value == _selectedClassMetric)
                    return;
                _selectedClassMetric = value;
                OnPropertyChanged("SelectedClassMetric");

                //notify change event
                if (IsAutomaticRefreshEnabled && RefreshCommand.CanExecute(null))
                    RefreshCommand.Execute(null);
            }
        }
        /// <summary>
        /// Current selected interface metric
        /// </summary>
        public LanguageAnalyzer.Metrics SelectedInterfaceMetric
        {
            get { return _selectedInterfaceMetric; }
            set
            {
                if (value == _selectedInterfaceMetric)
                    return;
                _selectedInterfaceMetric = value;
                OnPropertyChanged("SelectedInterfaceMetric");

                //notify change event
                if (IsAutomaticRefreshEnabled && RefreshCommand.CanExecute(null))
                    RefreshCommand.Execute(null);
            }
        }
        /// <summary>
        /// Current selected package metric
        /// </summary>
        public LanguageAnalyzer.Metrics SelectedPackageMetric
        {
            get { return _selectedPackageMetric; }
            set
            {
                if (value == _selectedPackageMetric)
                    return;
                _selectedPackageMetric = value;
                OnPropertyChanged("SelectedPackageMetric");

                //notify change event
                if (IsAutomaticRefreshEnabled && RefreshCommand.CanExecute(null))
                    RefreshCommand.Execute(null);
            }
        }
        /// <summary>
        /// Current selected struct metric
        /// </summary>
        public LanguageAnalyzer.Metrics SelectedStructMetric
        {
            get { return _selectedStructMetric; }
            set
            {
                if (value == _selectedStructMetric)
                    return;
                _selectedStructMetric = value;
                OnPropertyChanged("SelectedStructMetric");

                //notify change event
                if (IsAutomaticRefreshEnabled && RefreshCommand.CanExecute(null))
                    RefreshCommand.Execute(null);
            }
        }

        /// <summary>
        /// True if contained classes are shown
        /// </summary>
        public bool ShowContainedClasses
        {
            get { return _showContainedClasses; }
            set
            {
                if (value == _showContainedClasses)
                    return;
                _showContainedClasses = value;
                OnPropertyChanged("ShowContainedClasses");

                //propagate change
                if (!value)
                {
                    ShowClassMembers = false;
                    ShowClassMetrics = false;
                }

                //notify change event
                if (IsAutomaticRefreshEnabled && RefreshCommand.CanExecute(null))
                    RefreshCommand.Execute(null);
            }
        }
        /// <summary>
        /// True if contained interfaces are shown
        /// </summary>
        public bool ShowContainedInterfaces
        {
            get { return _showContainedInterfaces; }
            set
            {
                if (value == _showContainedInterfaces)
                    return;
                _showContainedInterfaces = value;
                OnPropertyChanged("ShowContainedInterfaces");

                //propagate change
                if (!value)
                {
                    ShowInterfaceMembers = false;
                    ShowInterfaceMetrics = false;
                }

                //notify change event
                if (IsAutomaticRefreshEnabled && RefreshCommand.CanExecute(null))
                    RefreshCommand.Execute(null);
            }
        }
        /// <summary>
        /// True if contained packages are shown
        /// </summary>
        public bool ShowContainedPackages
        {
            get { return _showContainedPackages; }
            set
            {
                if (value == _showContainedPackages)
                    return;
                _showContainedPackages = value;
                OnPropertyChanged("ShowContainedPackages");

                //notify change event
                if (IsAutomaticRefreshEnabled && RefreshCommand.CanExecute(null))
                    RefreshCommand.Execute(null);
            }
        }
        /// <summary>
        /// True if contained structs are shown
        /// </summary>
        public bool ShowContainedStructs
        {
            get { return _showContainedStructs; }
            set
            {
                if (value == _showContainedStructs)
                    return;
                _showContainedStructs = value;
                OnPropertyChanged("ShowContainedStructs");

                //propagate change
                if (!value)
                {
                    ShowStructMembers = false;
                    ShowStructMetrics = false;
                }

                //notify change event
                if (IsAutomaticRefreshEnabled && RefreshCommand.CanExecute(null))
                    RefreshCommand.Execute(null);
            }
        }
        /// <summary>
        /// True if internal class members are shown
        /// </summary>
        public bool ShowClassMembers
        {
            get { return _showClassMembers; }
            set
            {
                if (value == _showClassMembers)
                    return;
                _showClassMembers = value;
                OnPropertyChanged("ShowClassMembers");

                //propagate change
                if (!value)
                {
                    ShowClassMethodParameters = false;
                    ShowClassTypes = false;
                }

                //notify change event
                if (IsAutomaticRefreshEnabled && RefreshCommand.CanExecute(null))
                    RefreshCommand.Execute(null);
            }
        }
        /// <summary>
        /// True if parameters of class' methods are shown
        /// </summary>
        public bool ShowClassMethodParameters
        {
            get { return _showClassMethodParameters; }
            set
            {
                if (value == _showClassMethodParameters)
                    return;
                _showClassMethodParameters = value;
                OnPropertyChanged("ShowClassMethodParameters");

                //notify change event
                if (IsAutomaticRefreshEnabled && RefreshCommand.CanExecute(null))
                    RefreshCommand.Execute(null);
            }
        }
        /// <summary>
        /// True if class metrics are shown
        /// </summary>
        public bool ShowClassMetrics
        {
            get { return _showClassMetrics; }
            set
            {
                if (value == _showClassMetrics)
                    return;
                _showClassMetrics = value;
                OnPropertyChanged("ShowClassMetrics");

                //notify change event
                if (IsAutomaticRefreshEnabled && RefreshCommand.CanExecute(null))
                    RefreshCommand.Execute(null);
            }
        }
        /// <summary>
        /// True if the class member types are shown
        /// </summary>
        public bool ShowClassTypes
        {
            get { return _showClassTypes; }
            set
            {
                if (value == _showClassTypes)
                    return;
                _showClassTypes = value;
                OnPropertyChanged("ShowClassTypes");

                //notify change event
                if (IsAutomaticRefreshEnabled && RefreshCommand.CanExecute(null))
                    RefreshCommand.Execute(null);
            }
        }
        /// <summary>
        /// True if interface members are shown
        /// </summary>
        public bool ShowInterfaceMembers
        {
            get { return _showInterfaceMembers; }
            set
            {
                if (value == _showInterfaceMembers)
                    return;
                _showInterfaceMembers = value;
                OnPropertyChanged("ShowInterfaceMembers");

                //propagate change
                if (!value)
                {
                    ShowInterfaceMethodParameters = false;
                    ShowInterfaceTypes = false;
                }

                //notify change event
                if (IsAutomaticRefreshEnabled && RefreshCommand.CanExecute(null))
                    RefreshCommand.Execute(null);
            }
        }
        /// <summary>
        /// True if parameters of interface's methods are shown
        /// </summary>
        public bool ShowInterfaceMethodParameters
        {
            get { return _showInterfaceMethodParameters; }
            set
            {
                if (value == _showInterfaceMethodParameters)
                    return;
                _showInterfaceMethodParameters = value;
                OnPropertyChanged("ShowInterfaceMethodParameters");

                //notify change event
                if (IsAutomaticRefreshEnabled && RefreshCommand.CanExecute(null))
                    RefreshCommand.Execute(null);
            }
        }
        /// <summary>
        /// True if interface metrics are shown
        /// </summary>
        public bool ShowInterfaceMetrics
        {
            get { return _showInterfaceMetrics; }
            set
            {
                if (value == _showInterfaceMetrics)
                    return;
                _showInterfaceMetrics = value;
                OnPropertyChanged("ShowInterfaceMetrics");

                //notify change event
                if (IsAutomaticRefreshEnabled && RefreshCommand.CanExecute(null))
                    RefreshCommand.Execute(null);
            }
        }
        /// <summary>
        /// True if the interface member types are shown
        /// </summary>
        public bool ShowInterfaceTypes
        {
            get { return _showInterfaceTypes; }
            set
            {
                if (value == _showInterfaceTypes)
                    return;
                _showInterfaceTypes = value;
                OnPropertyChanged("ShowInterfaceTypes");

                //notify change event
                if (IsAutomaticRefreshEnabled && RefreshCommand.CanExecute(null))
                    RefreshCommand.Execute(null);
            }
        }
        /// <summary>
        /// True if package metrics are shown
        /// </summary>
        public bool ShowPackageMetrics
        {
            get { return _showPackageMetrics; }
            set
            {
                if (value == _showPackageMetrics)
                    return;
                _showPackageMetrics = value;
                OnPropertyChanged("ShowPackageMetrics");

                //notify change event
                if (IsAutomaticRefreshEnabled && RefreshCommand.CanExecute(null))
                    RefreshCommand.Execute(null);
            }
        }
        /// <summary>
        /// True if struct members are shown
        /// </summary>
        public bool ShowStructMembers
        {
            get { return _showStructMembers; }
            set
            {
                if (value == _showStructMembers)
                    return;
                _showStructMembers = value;
                OnPropertyChanged("ShowStructMembers");

                //propagate change
                if (!value)
                {
                    ShowStructMethodParameters = false;
                    ShowStructTypes = false;
                }

                //notify change event
                if (IsAutomaticRefreshEnabled && RefreshCommand.CanExecute(null))
                    RefreshCommand.Execute(null);
            }
        }
        /// <summary>
        /// True if parameters of struct's methods are shown
        /// </summary>
        public bool ShowStructMethodParameters
        {
            get { return _showStructMethodParameters; }
            set
            {
                if (value == _showStructMethodParameters)
                    return;
                _showStructMethodParameters = value;
                OnPropertyChanged("ShowStructMethodParameters");

                //notify change event
                if (IsAutomaticRefreshEnabled && RefreshCommand.CanExecute(null))
                    RefreshCommand.Execute(null);
            }
        }
        /// <summary>
        /// True if struct metrics are shown
        /// </summary>
        public bool ShowStructMetrics
        {
            get { return _showStructMetrics; }
            set
            {
                if (value == _showStructMetrics)
                    return;
                _showStructMetrics = value;
                OnPropertyChanged("ShowStructMetrics");

                //notify change event
                if (IsAutomaticRefreshEnabled && RefreshCommand.CanExecute(null))
                    RefreshCommand.Execute(null);
            }
        }
        /// <summary>
        /// True if the struct member types are shown
        /// </summary>
        public bool ShowStructTypes
        {
            get { return _showStructTypes; }
            set
            {
                if (value == _showStructTypes)
                    return;
                _showStructTypes = value;
                OnPropertyChanged("ShowStructTypes");

                //notify change event
                if (IsAutomaticRefreshEnabled && RefreshCommand.CanExecute(null))
                    RefreshCommand.Execute(null);
            }
        }
        /// <summary>
        /// True if external dependencies are shown
        /// </summary>
        public bool ShowExternalDependencies
        {
            get { return _showExternalDependencies; }
            set
            {
                if (value == _showExternalDependencies)
                    return;
                _showExternalDependencies = value;
                OnPropertyChanged("ShowExternalDependencies");

                //propagate change
                if (!value)
                    ShowPrimitiveTypes = false;

                //notify change event
                if (IsAutomaticRefreshEnabled && RefreshCommand.CanExecute(null))
                    RefreshCommand.Execute(null);
            }
        }
        /// <summary>
        /// True if primitive types are shown
        /// </summary>
        public bool ShowPrimitiveTypes
        {
            get { return _showPrimitiveTypes; }
            set
            {
                if (value == _showPrimitiveTypes)
                    return;
                _showPrimitiveTypes = value;
                OnPropertyChanged("ShowPrimitiveTypes");

                //notify change event
                if (IsAutomaticRefreshEnabled && RefreshCommand.CanExecute(null))
                    RefreshCommand.Execute(null);
            }
        }

        /// <summary>
        /// Get the current selected package
        /// </summary>
        public IElementBase SelectedElement
        { get { return _selectedElement; } }

        #endregion

        #region Commands

        public ICommand RefreshCommand
        {
            get
            {
                if (_refreshCommand == null)
                    _refreshCommand = new RelayCommand(
                        param => notifyRefresh(),
                        param => notifyRefresh != null);
                return _refreshCommand;
            }
        }

        #endregion

        #region Notifier methods

        private void NotifyElementSelection(IElementBase selectedElement)
        {
            if (selectedElement == _selectedElement ||
                (selectedElement.Kind != ElementKinds.Project) &&
                (selectedElement.Kind != ElementKinds.Namespace))
                return;
            _selectedElement = selectedElement;

            //notify change event
            if (IsAutomaticRefreshEnabled && RefreshCommand.CanExecute(null))
                RefreshCommand.Execute(null);
        }

        #endregion
    }
}
